//https://github.com/zuoyanart/sparender
// 载入puppeteer
const puppeteer = require("puppeteer-extra");
const genericPool = require("generic-pool");
const { PendingXHR } = require("pending-xhr-puppeteer");
const StealthPlugin = require("puppeteer-extra-plugin-stealth");
puppeteer.use(StealthPlugin());

module.exports = class {
  /**
   * 初始化一个 Puppeteer 池
   * @param {Object} [options.puppeteerArgs={}] puppeteer.launch 启动的参数
   * @param {Function} [options.validator=(instance)=>Promise.resolve(true))] 用户自定义校验 参数是 取到的一个实例
   * @param {Object} [options.otherConfig={}] 剩余的其他参数
   * @return {Object} pool
   */
  initPuppeteerPool() {
    if (global.pptPoll) {
      global.pptPoll.drain().then(() => global.pptPoll.clear());
    }
    const options = Object.assign(this.config, {
      validator: () => Promise.resolve(true),
    });
    const factory = {
      create: () =>
        puppeteer.launch(options.start).then((instance) => {
          // 创建一个 puppeteer 实例 ，并且初始化使用次数为 0
          instance.useCount = 0;
          return instance;
        }),
      destroy: (instance) => {
        instance.close();
        console.log("关闭instance");
      },
      validate: (instance) => {
        // 执行一次自定义校验，并且校验 实例已使用次数。 当 返回 reject 时 表示实例不可用
        return options
          .validator(instance)
          .then((valid) => {

            return Promise.resolve(
              instance.connected
              &&
              valid &&
              (options.maxUses <= 0 || instance.useCount < options.maxUses)
            )
          });

      },
    };
    const pool = genericPool.createPool(factory, options);
    const genericAcquire = pool.acquire.bind(pool);
    // 重写了原有池的消费实例的方法。添加一个实例使用次数的增加
    pool.acquire = () =>
      genericAcquire().then((instance) => {
        instance.useCount += 1;
        return instance;
      });

    pool.use = (fn) => {
      let resource;
      return pool
        .acquire()
        .then((r) => {
          resource = r;
          return resource;
        })
        .then(fn)
        .then(
          (result) => {
            // 不管业务方使用实例成功与后都表示一下实例消费完成
            pool.release(resource);
            return result;
          },
          (err) => {
            pool.release(resource);
            throw err;
          }
        );
    };
    return pool;
  }

  async init() {
    global.pptPoll = this.initPuppeteerPool();
  }

  config = {
    max: 1, //最多产生多少个 puppeteer 实例 。如果你设置它，请确保 在引用关闭时调用清理池。 pool.drain().then(()=>pool.clear())
    min: 1, //保证池中最少有多少个实例存活
    maxUses: 1000, //每一个 实例 最大可重用次数，超过后将重启实例。0表示不检验
    testOnBorrow: true, // 在将 实例 提供给用户之前，池应该验证这些实例。
    autostart: false, //是不是需要在 池 初始化时 初始化 实例
    idleTimeoutMillis: 1000 * 60 * 60, //如果一个实例 60分钟 都没访问就关掉他
    evictionRunIntervalMillis: 1000 * 60 * 3, //每 3分钟 检查一次 实例的访问状态
    start: {
      headless: "new",
      protocolTimeout: 100000, // 增加 protocolTimeout 的值
      devtools: false,
      ignoreHTTPSErrors: true,
      args: [
        "--disable-gpu",
        "--disable-dev-shm-usage",
        '--disable-accelerated-2d-canvas',
        "--disable-setuid-sandbox",
        "--disable-xss-auditor", //关闭 XSS Auditor
        "--no-first-run",
        "--no-sandbox",
        "--disable-extensions",
        "--no-zygote",
        //'--single-process',
        "--disable-web-security",
        "--blink-settings=imagesEnabled=false", //不渲染css
        "--enable-features=NetworkService",
        "--disable-popup-blocking",
        "--disable-crash-reporter",
        "--disable-software-rasterizer",
        "--disable-notifications",
        "--disable-session-crashed-bubbl"
      ],
    },
  };

  /**
   *
   */
  async htmlRender(url = "http://127.0.0.1:8181/", isMobile = false) {
    // 在业务中取出实例使用
    return await global.pptPoll.use(async (instance) => {
      return new Promise(async (resolve, reject) => {
        const page = await instance.newPage();
        try {
          //如果是移动端则设置UA和视图
          if (isMobile) {
            //let iPhoneX = tools.config("mobileRender");
            //await page.setUserAgent(iPhoneX.userAgent);
            // await page.setViewport(iPhoneX.viewport);
          }
          // 开启请求拦截, 无头模式下有效
          const blockTypes = [
            "image",
            "media",
            "font",
            "manifest",
            "other",
            "texttrack",
            "websocket",
          ];
          await page.setRequestInterception(true); //开启请求拦截
          page.on("request", async (request) => {
            const type = request.resourceType();
            if (blockTypes.indexOf(type) > -1) {
              //直接阻止请求
              return request.abort();
            } else {
              return request.continue({
                headers: Object.assign({}, request.headers(), {
                  ppt: "true",
                }),
              });
            }
          });
          //响应
          page.on("response", async (response) => {
            // console.log(
            //   new Date().toLocaleString() +
            //   " " +
            //   (await response.url()) +
            //   " " +
            //   (await response.request().method()) +
            //   " " +
            //   (await response.status()) +
            //   " " +
            //   (await response.request().resourceType())
            // );
          });
          try {
            await page.goto(url, {
              waitUntil: "networkidle0",
            });
          } catch (e) { }

          //保存屏幕截屏
          // await page.screenshot({
          //   path: "./dashboard_shot.png",
          //   clip: {
          //     x: 200,
          //     y: 60,
          //     width: 780,
          //     height: 3405,
          //   },
          // });
          const pendingXHR = new PendingXHR(page);
          await pendingXHR.waitForAllXhrFinished();
          const html = await page.content();
          resolve(html);
        } catch (e) {
          console.log(new Date().toLocaleString() + " " + e);
          reject(e);
        } finally {
          if (page != null) {
            page.close();
            console.log("关闭tab page");
          }
        }
      });
    });
  }
};
